package com.tanklab.signature.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tanklab.signature.common.util.ChainUtils;
import com.tanklab.signature.common.util.HashUtil;
import com.tanklab.signature.common.util.ResultCode;
import com.tanklab.signature.common.util.TimeUtils;
import com.tanklab.signature.ds.req.UploadFileReq;
import com.tanklab.signature.ds.resp.CommonResp;
import com.tanklab.signature.entity.File;
import com.tanklab.signature.entity.Request;
import com.tanklab.signature.entity.User;
import com.tanklab.signature.eth.ChainConnection;
import com.tanklab.signature.mapper.FileMapper;
import com.tanklab.signature.mapper.RequestMapper;
import com.tanklab.signature.mapper.UserMapper;
import com.tanklab.signature.service.FileUploadService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;


@Service
public class FileUploadServiceImpl implements FileUploadService {

    @Value("${file.upload-dir:./uploads/files/}")
    private String fileUploadDir;

    @Value("${file.access-path:/files/}")
    private String fileAccessPath;

    @Value("${server.base-url:http://localhost:8010/signature}")
    private String baseUrl;

    @Autowired
    private FileMapper fileMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RequestMapper requestMapper;

    @Autowired
    private ChainUtils chainUtils;

    @Override
    @Transactional
    public CommonResp uploadFile(MultipartFile file, String fileName, UploadFileReq uploadFileReq, String openid) {
        CommonResp resp = new CommonResp();

        try {
            // 1. 查询用户信息
            QueryWrapper<User> userQuery = new QueryWrapper<>();
            userQuery.eq("openid", openid);
            User user = userMapper.selectOne(userQuery);

            if (user == null) {
                resp.setRet(ResultCode.USER_NOT_EXIST);
                resp.setMsg("用户不存在");
                return resp;
            }

            // 2. 保存文件到服务器
            String storedFileName = storeFile(file, fileName);
            String fileStoragePath = fileAccessPath + storedFileName;
            String fullStoragePath = baseUrl + fileStoragePath;

            // 3. 计算文件内容的哈希值
            String fileContentHash = calculateFileContentHash(file);

            // 4. 构造文件信息JSON并计算哈希
            JSONObject fileInfo = new JSONObject();
            fileInfo.put("fileName", fileName);
            fileInfo.put("fileType", uploadFileReq.getFileType());
            fileInfo.put("fileDescription", uploadFileReq.getFileDescription());
            fileInfo.put("uploaderOpenid", openid);
            fileInfo.put("createdTime", TimeUtils.getCurrentTime());

            // 5. 计算文件信息的哈希值
            String fileInfoHash = HashUtil.hash_func(fileInfo.toString(), "MD5");

            // 6. 处理文件内容上链
            String contentTxId = chainUtils.sendHashToChain(fileContentHash, "fileContentHash");


            // 7. 处理文件信息上链
            String infoTxId = chainUtils.sendHashToChain(fileInfoHash, "fileInfoHash");


            // 8. 创建文件实体并保存
            File fileEntity = new File()
                    .setNameFile(fileName)
                    .setFilePath(fileStoragePath)
                    .setFileType(uploadFileReq.getFileType())
                    .setFileDescription(uploadFileReq.getFileDescription())
                    .setCreatedTime(TimeUtils.getCurrentTime())
                    .setFileContentHash(fileContentHash)
                    .setContentTxId(contentTxId)
                    .setFileInfoHash(fileInfoHash)
                    .setInfoTxId(infoTxId)
                    .setUploaderOpenid(openid);

            fileMapper.insert(fileEntity);

            // 9. 自动创建申请
            // 构建申请信息JSON
            JSONObject requestInfo = new JSONObject();
            requestInfo.put("fileId", fileEntity.getIdFile());
            requestInfo.put("fileType", uploadFileReq.getFileType());
            requestInfo.put("initiatorOpenid", openid);
            requestInfo.put("createdTime", TimeUtils.getCurrentTime());

            // 计算申请信息哈希值
            String requestInfoHash = HashUtil.hash_func(requestInfo.toString(), "MD5");

            // 申请信息上链
            String requestTxId = chainUtils.sendHashToChain(requestInfoHash, "requestInfoHash");


            // 创建申请实体
            Request request = new Request()
                    .setFileId(fileEntity.getIdFile())
                    .setNameRequest(fileName)  // 使用文件名作为申请名称
                    .setTypeRequest(uploadFileReq.getFileType())
                    .setInitiatorOpenid(openid)
                    .setCreatedTime(TimeUtils.getCurrentTime())
                    .setStatus("pending")  // 初始状态为待审批
                    .setRequestInfoHash(requestInfoHash)
                    .setRequestTxId(requestTxId);

            requestMapper.insert(request);

            // 更新文件关联的申请ID
            fileEntity.setRequestId(request.getIdRequest());
            fileMapper.updateById(fileEntity);

            // 10. 构造返回信息
            JSONObject data = new JSONObject();
            data.put("fileId", fileEntity.getIdFile());
            data.put("fileName", fileEntity.getNameFile());
            data.put("fileType", fileEntity.getFileType());
            data.put("filePath", fullStoragePath);
            data.put("initiatorName", user.getUser_name());
            data.put("initiatorIdentity", user.getAuthority() == 0 ? "申请者" : "审批者");
            data.put("fileDescription", fileEntity.getFileDescription());
            data.put("createTime", fileEntity.getCreatedTime());
            data.put("status", "pending");  // 状态为待审批
            data.put("fileContentHash", fileEntity.getFileContentHash());
            data.put("contentTxId", fileEntity.getContentTxId());
            data.put("fileInfoHash", fileEntity.getFileInfoHash());
            data.put("infoTxId", fileEntity.getInfoTxId());
            data.put("requestId", request.getIdRequest());

            resp.setRet(ResultCode.SUCCESS);
            resp.setMsg("文件上传并创建申请成功");
            resp.setData(data);

        } catch (Exception e) {
            e.printStackTrace();
            resp.setRet(ResultCode.ERROR);
            resp.setMsg("文件上传失败: " + e.getMessage());
        }

        return resp;
    }

    /**
     * 存储文件并返回文件名
     */
    private String storeFile(MultipartFile file, String originalFileName) throws IOException {
        // 清理文件名
        String fileName = StringUtils.cleanPath(originalFileName);

        // 防止路径穿越攻击
        if (fileName.contains("..")) {
            throw new IOException("文件名包含无效路径序列: " + fileName);
        }

        // 生成唯一文件名
        String fileExtension = "";
        if (fileName.contains(".")) {
            fileExtension = fileName.substring(fileName.lastIndexOf("."));
        }
        String storedFileName = UUID.randomUUID().toString() + fileExtension;

        // 确保目录存在
        Path uploadPath = Paths.get(fileUploadDir);
        if (!Files.exists(uploadPath)) {
            Files.createDirectories(uploadPath);
        }

        // 保存文件
        Path filePath = uploadPath.resolve(storedFileName);
        Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING);

        return storedFileName;
    }

    /**
     * 计算文件内容的哈希值
     */
    private String calculateFileContentHash(MultipartFile file) throws IOException, NoSuchAlgorithmException {
        MessageDigest digest = MessageDigest.getInstance("MD5");
        byte[] byteArray = new byte[1024];
        int bytesCount = 0;

        // 从文件读取字节并更新哈希值
        java.io.InputStream inputStream = file.getInputStream();
        while ((bytesCount = inputStream.read(byteArray)) != -1) {
            digest.update(byteArray, 0, bytesCount);
        }
        inputStream.close();

        // 完成哈希计算并转换为十六进制字符串
        byte[] bytes = digest.digest();
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }
}